#include <malloc.h>
#include <string.h>
#include <stdio.h>
#include "buildsettings.h"
#include "hashtable.h"
#include "errorcodes.h"

/*! Create a hash table
  /param size Size of the hash table's array
  /return The new HashTable object. NOTE: Not a pointer
*/
struct structHashTable new_HashTable(int size)
{
	struct structHashTable ht;
	ht.Size = size;
	ht.Pairs = calloc(size, sizeof(ADDRESS));
	ht.Set = &hashtable_set;
	ht.Get = &hashtable_get;
	ht.GetKeys = &hashtable_getkeys;

	return ht;
}
/*! Destory a hash table
  /param ht A pointer to the hash table to deconstruct
*/
void destroy_HashTable(struct structHashTable *ht)
{
	int i;
	struct structHashTablePair *pair;
	struct structHashTablePair *lastPair;
	for (i=0; i<ht->Size; i++)
	{
		pair = ht->Pairs[i];
		while (pair != 0x0)
		{
			lastPair = pair;
			pair = pair->Next;
			free(lastPair->Value);
			lastPair->Value=0x0;
			free(lastPair);
		}
	}
}

List hashtable_getkeys(struct structHashTable *ht)
{
	List set = new(List);
	int i;
	struct structHashTablePair *pair;

	for (i=0; i<ht->Size; i++)
	{
		pair = ht->Pairs[i];
		while (pair != 0x0)
		{
			set.Append(&set, pair->Key);
			pair = pair->Next;
		}
	}
	return set;
}

/*!
  A generic string hash function.
  /param key A null terminated string to hash
  /return An integer hash value
*/
int Hash(char *key)
{
	int m=1;
	int count=0;
	char ch=key[0];
	while (ch != '\0')
	{
		count=count+1;
		m=m*ch;
		ch = key[count];
	}
	if (m < 0)
		m=m*(-1);
	return m;
}

/*!
  Set a value in a hash table.
  /param ht Hash table on which to perform operations
  /param key A unique null-terminated string key
  /param value A pointer to a dynamically-allocated memory block
  /return An error code based on the project error codes. Defined in errorcodes.h
*/
int hashtable_set(struct structHashTable *ht, char *key, void *value)
{
	struct structHashTablePair *iter;
	struct structHashTablePair *pair;
	int hashValue=Hash(key);
	int virtIndex=hashValue%ht->Size;

	if (ht->Pairs[virtIndex] == 0x0)
	{
		//printf("Setting new bucket %d...", virtIndex);
		pair = (struct structHashTablePair*)malloc(sizeof(HashTablePair));
		pair->Value = value;
		pair->Key = (char*)malloc(strlen(key));
		memcpy(pair->Key, key, strlen(key)+1);
		pair->Next=0x0;

		ht->Pairs[virtIndex] = pair;
		//printf("Done!\n");
		return EC_SUCCESS;
	}
	else {
		//printf("iterating %d...\n", virtIndex);
		iter=ht->Pairs[virtIndex];
		//printf("::0x%0X\n", iter);
		//printf("::%s\n", key);
		//printf("::%s\n", iter->Key);
		while (iter->Next != 0x0 && strcmp(iter->Key, key) != 0) {
			//printf("next\n");
			iter=iter->Next;
		}
		if (strcmp(iter->Key, key) == 0) {
			//printf("Replacing old value..");
			if (iter->Value != 0x0)
				free(iter->Value);
			iter->Value = value;
			//printf("Done!\n");
		}
		else {
			//printf("Appending new value...");
			pair = (HashTablePair*)malloc(sizeof(HashTablePair));
			pair->Key = key;
			pair->Value = value;
			pair->Next = 0x0;

			iter->Next = pair;
			//printf("Done!\n");
		}
	}

	return EC_SUCCESS;
}
/*!
  Returns a pointer to a pointer to the value.
  /param ht Hash table on which to perform operations
  /param key A unique null-terminated string key
  /return A pointer to the value, if successful, null otherwise
*/
void *hashtable_get(struct structHashTable *ht, char *key)
{
	struct structHashTablePair *iter;
	int hashValue = Hash(key);
	int virtIndex=hashValue%ht->Size;

	if (ht->Pairs[virtIndex] == 0x0)
	{
		return 0x0;
	}
	else {
		iter=ht->Pairs[virtIndex];
		while (iter->Next != 0x0 && strcmp(iter->Key, key) != 0) {
			iter=iter->Next;
		}
		if (strcmp(iter->Key, key) == 0) {
			return iter->Value;
		}
	}
	return 0x0;
}
